<?php

declare(strict_types=1);

namespace Tool\Database;

use PDO;
use PDOException;
use PDOStatement;

/**
 * 数据库连接.
 */
class Connection
{
    private ?PDO $conn;

    private int $transactionCount = 0;

    public function __construct(DSN $dsn, string $user, string $pass)
    {
        try {
            $opt = [
                PDO::ATTR_PERSISTENT => true, // true:长连接
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // 抛异常
                PDO::ATTR_CASE => PDO::CASE_NATURAL, // 返回字段保持原来大小写
                PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL, // null值不转空字符串
                PDO::ATTR_STRINGIFY_FETCHES => false, // false:所有值不强转字符串
                PDO::ATTR_EMULATE_PREPARES => false, // false:使用MYSQL预处理
            ];
            $this->conn = new PDO((string) $dsn, $user, $pass, $opt);
        } catch (PDOException $e) {
            throw Exception::make($e);
        }
    }

    public function __destruct()
    {
        $this->conn = null;
    }

    /**
     * 获取预处理语句.
     */
    public function getPDOStatement(string $sql, array &$options = []): PDOStatement
    {
        return $this->conn->prepare($sql, $options);
    }

    /**
     * 开启事务
     */
    public function beginTransaction(): bool
    {
        if (!$this->transactionCount++) {
            return $this->conn->beginTransaction();
        }
        $this->conn->exec('SAVEPOINT trans'.$this->transactionCount);

        return true;
    }

    /**
     * 提交事务
     */
    public function commit(): bool
    {
        if (!--$this->transactionCount) {
            return $this->conn->commit();
        }

        return true;
    }

    /**
     * 回滚事务
     */
    public function rollback(): bool
    {
        if (!--$this->transactionCount) {
            return $this->conn->rollback();
        }
        $this->conn->exec('ROLLBACK TO trans'.($this->transactionCount + 1));

        return true;
    }
}
